#include "thread.h"
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
#define VERBOSE 0

struct tableau {

    int * tab;
    int taille;
    int debut;
};

void print_tab (int * tab, int debut, int taille) 
{
  int i;
  
  for (i=debut; i<taille; i++){
  	if(VERBOSE)
    	fprintf (stdout, "| %d |", tab[i]);  
  }
  if(VERBOSE)
  	fprintf (stdout, "\n");
}
static void * threadtri(void * arg)
{
  struct tableau * tab = (struct tableau *)arg;
  int err, taille1, taille2;
  thread_t th1,th2;

  if (tab->taille == 1)
    thread_exit(NULL);

  struct tableau *tab1 = (struct tableau*) malloc (sizeof (struct tableau));
  struct tableau *tab2 = (struct tableau*) malloc (sizeof (struct tableau));

  tab1->tab = tab->tab;
  tab2->tab = tab->tab;
  taille1 = tab1->taille = (int) (tab->taille/2);
  taille2 = tab2->taille = (int) (tab->taille - taille1);
  //    fprintf (stderr, "taille1 : %d\n", taille1);
  //    fprintf (stderr, "taille2 : %d\n", taille2);
  tab1->debut = tab->debut;
  tab2->debut = tab->debut+taille1;

  err = thread_create(&th1, threadtri, (void *)tab1);
  assert(!err);
  err = thread_create(&th2, threadtri, (void *)tab2);
  assert(!err);

  err = thread_join(th1, NULL);
  assert(!err);
  err = thread_join(th2, NULL);
  assert(!err);

  int tmp[tab->taille];
  int indice1=(tab1->debut), indice2=(tab2->debut);
  int i;
  
  //    fprintf (stderr, "tab->debut = %d, tab->taille = %d\n", tab->debut, tab->taille);

  //    fprintf (stderr, "tab1\n");
  //    print_tab (tab1->tab, tab1->debut, tab1->taille);
  //    fprintf (stderr, "tab2\n");
  //    print_tab (tab2->tab, tab2->debut, tab2->taille);

  //    fprintf (stderr, "tmp\n");
  //    print_tab (tmp, tab->debut, tab->taille);

  for (i = 0; i < tab->taille; i++){
      
    if((indice1<tab1->debut+tab1->taille) && 
	 ((indice2 >= tab2->debut+tab2->taille) || (tab->tab[indice1] <= tab->tab[indice2]))){
          //    fprintf (stderr, "tab->debut+i = %d, indice1 = %d, tmp[tab->debut+i] = %d, tab->tab[indice1] = %d\n", tab->debut+i, indice1, tmp[tab->debut+i], tab->tab[indice1]);
          tmp[i] = tab->tab[indice1++];
      }
   
      else{
      
          //    fprintf (stderr, "tab->debut+i = %d, indice1 = %d, tmp[tab->debut+i] = %d, tab->tab[indice1] = %d\n", tab->debut+i, indice1, tmp[tab->debut+i], tab->tab[indice1]);
          tmp[i] = tab->tab[indice2++];
      }
  }

  //    fprintf (stderr, "tab1\n");
  //    print_tab (tab1->tab, tab1->debut, tab1->taille);
  //    fprintf (stderr, "tab2\n");
  //    print_tab (tab2->tab, tab2->debut, tab2->taille);

  //    fprintf (stderr, "tmp\n");
  //    print_tab (tmp, tab->debut, tab->taille);

    for(i=0; i<tab->taille; i++)
    tab->tab[tab->debut+i]=tmp[i];

    free(tab1);
    free(tab2);
    thread_exit(NULL);

}
                      
int* init_tab (int * tab, int taille) 
{
  int i;
  tab = malloc (taille * sizeof (int));
  if(VERBOSE)
  	printf ("tab = %p\n", tab);  
  if (tab == NULL) exit (0);
  
  srand(time(NULL));
  for (i=0; i<taille; i++)
  {
    tab[i] = (int)(unsigned char) rand();
  }

  return tab;
}

int main(int argc, char *argv[])
{
  thread_t th;
  int err;
  
  if (argc != 2){
  	if(VERBOSE)
    	printf("Passez la taille du tableau à trier en argument\n");
    return 1;
  }

  struct tableau *tab = malloc (sizeof (struct tableau));
  if (tab == NULL) return 1;
  tab->taille = atoi (argv[1]);
  tab->debut = 0;
  tab->tab = init_tab (tab->tab, tab->taille);

  print_tab (tab->tab, tab->debut, tab->taille);

  init();
  if(VERBOSE)
  	printf("creation de thread\n");
  err = thread_create(&th, threadtri, (void *)tab);
  assert(!err);
  if(VERBOSE)
  	printf("lancement du thread de tri %p\n", th);

  err = thread_join(th, NULL);
  assert(!err);
  if(VERBOSE)
  	printf("les threads ont terminé et le tableau est un peu trié\n");

  print_tab (tab->tab, tab->debut, tab->taille);
  free(tab->tab);
  free(tab);

  return 0;
}
